﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;

namespace Meta.Numerics.Statistics
{
    /// <summary>
    /// Extension class providing ADO.NET data reader methods for the Meta Numerics sample classes.
    /// </summary>
    public static class SampleDataReaderExtensions
    {
        /// <summary>
        /// Loads values from a data reader to a univariate sample.
        /// </summary>
        /// <param name="sample">Univariate sample.</param>
        /// <param name="reader">The data reader.</param>
        /// <param name="dbIndex">The column number.</param>
        public static void Load(this Sample sample, IDataReader reader, int dbIndex)
        {
            if (reader == null) throw new ArgumentNullException("reader");
            while (reader.Read())
            {
                if (reader.IsDBNull(dbIndex)) continue;
                object value = reader.GetValue(dbIndex);
                sample.Add(Convert.ToDouble(value, CultureInfo.InvariantCulture));
            }
        }

        /// <summary>
        /// Loads values from a data reader to a bivariate sample.
        /// </summary>
        /// <param name="sample">Bivariate sample.</param>
        /// <param name="reader">The data reader.</param>
        /// <param name="xIndex">The column number of the x-variable.</param>
        /// <param name="yIndex">The column number of the y-variable.</param>
        public static void Load(this BivariateSample sample, IDataReader reader, int xIndex, int yIndex)
        {
            if (reader == null) throw new ArgumentNullException("reader");
            while (reader.Read())
            {
                if (reader.IsDBNull(xIndex) || reader.IsDBNull(yIndex)) continue;
                object xValue = reader.GetValue(xIndex);
                object yValue = reader.GetValue(yIndex);
                sample.Add(Convert.ToDouble(xValue, CultureInfo.InvariantCulture), Convert.ToDouble(yValue, CultureInfo.InvariantCulture));
            }
        }

        /// <summary>
        /// Loads values from a data reader to a multivariate sample.
        /// </summary>
        /// <param name="sample">Multivariate sample.</param>
        /// <param name="reader">The data reader.</param>
        /// <param name="dbIndexes">The database column indexes of the sample columns.</param>
        public static void Load(this MultivariateSample sample, IDataReader reader, IList<int> dbIndexes)
        {
            if (reader == null) throw new ArgumentNullException("reader");
            if (dbIndexes == null) throw new ArgumentNullException("dbIndexes");
            if (dbIndexes.Count != sample.Dimension) throw new DimensionMismatchException();

            // create an array to store values, which we will re-use as we move through the data
            double[] entry = new double[sample.Dimension];
            // move through the data
            while (reader.Read())
            {
                // check each entry and, if value, add it to the sample
                if (ReadValues(sample.Dimension, reader, dbIndexes, entry)) sample.Add(entry);
            }

        }

        /// <summary>
        /// Loads values from a data reader to a multivariate sample.
        /// </summary>
        /// <param name="sample">Multivariate sample.</param>
        /// <param name="reader">The data reader.</param>
        /// <param name="dbIndexes">The database column indexes of the sample columns.</param>
        public static void Load(this MultivariateSample sample, IDataReader reader, params int[] dbIndexes)
        {
            Load(sample, reader, (IList<int>)dbIndexes);
        }

        private static bool ReadValues(int dimension, IDataReader reader, IList<int> dbIndexes, double[] entry)
        {
            for (int c = 0; c < dimension; c++)
            {
                int i = dbIndexes[c];
                if (reader.IsDBNull(i))
                {
                    return (false);
                }
                else
                {
                    entry[c] = Convert.ToDouble(reader.GetValue(i), CultureInfo.InvariantCulture);
                }
            }
            return (true);
        }
    }
}